package com.jhsw.roadsun.module.business.service.user;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.jhsw.roadsun.framework.common.exception.ServiceException;
import com.jhsw.roadsun.framework.common.pojo.PageResult;
import com.jhsw.roadsun.framework.common.util.object.BeanUtils;
import com.jhsw.roadsun.module.business.controller.admin.user.vo.BizUserPageReqVO;
import com.jhsw.roadsun.module.business.controller.admin.user.vo.BizUserRespVO;
import com.jhsw.roadsun.module.business.controller.admin.user.vo.BizUserSaveReqVO;
import com.jhsw.roadsun.module.business.controller.app.user.vo.*;
import com.jhsw.roadsun.module.business.dal.dataobject.user.BizUserDO;
import com.jhsw.roadsun.module.business.dal.mysql.user.BizUserMapper;
import com.jhsw.roadsun.module.business.service.expressage.ExpressageService;
import com.jhsw.roadsun.module.business.service.useraddress.UserAddressService;
import com.jhsw.roadsun.module.business.util.wechat.WeChatLoginUtil;
import com.jhsw.roadsun.module.system.api.auth.AuthApi;
import com.jhsw.roadsun.module.system.api.auth.dto.AppAuthDTO;
import com.jhsw.roadsun.module.system.api.sms.SmsCodeApi;
import com.jhsw.roadsun.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
import com.jhsw.roadsun.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;

import static com.jhsw.roadsun.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.jhsw.roadsun.framework.common.util.servlet.ServletUtils.getClientIP;
import static com.jhsw.roadsun.module.business.enums.ErrorCodeConstants.*;
import static com.jhsw.roadsun.module.system.enums.sms.SmsSceneEnum.*;

/**
 * 用户信息 Service 实现类
 *
 * @author Ysugar
 */
@Slf4j
@Service
@Validated
public class BizUserServiceImpl implements BizUserService {

    @Resource
    private BizUserMapper bizUserMapper;
    @Resource
    @Lazy
    private ExpressageService expressageService;
    @Resource
    private UserAddressService userAddressService;
    @Resource
    private AuthApi authApi;
    @Resource
    private SmsCodeApi smsCodeApi;

    private static final ReentrantLock REGISTER_LOCK = new ReentrantLock();

    @Override
    public Long createBizUser(BizUserSaveReqVO createReqVO) {
        // 插入
        String mobile = createReqVO.getMobile();
        BizUserDO bizUser = BeanUtils.toBean(createReqVO, BizUserDO.class);
        if (StrUtil.isBlank(createReqVO.getNickname()))
            bizUser.setNickname(mobile);

        REGISTER_LOCK.lock();
        try {
            // 判断如果存在，则直接返回
            BizUserDO user = bizUserMapper.selectOne(BizUserDO::getMobile, mobile);
            if (Objects.nonNull(user))
                throw exception(BIZ_USER_MOBILE_IS_EXISTS);

            bizUserMapper.insert(bizUser);

            return bizUser.getId();
        } catch (ServiceException se) {
            throw se;
        } catch (Exception e) {
            log.error("用户新增失败", e);
            throw exception(BIZ_USER_SAVE_ERROR);
        } finally {
            if (REGISTER_LOCK.isLocked()) {
                REGISTER_LOCK.unlock();
            }
        }
    }

    @Override
    public void updateBizUser(BizUserSaveReqVO updateReqVO) {
        Long id = updateReqVO.getId();
        // 校验存在
        validateBizUserExists(id);

        String mobile = updateReqVO.getMobile();

        REGISTER_LOCK.lock();
        try {
            // 判断如果存在，则直接返回
            BizUserDO user = bizUserMapper.selectOne(Wrappers.<BizUserDO>lambdaQuery()
                    .eq(BizUserDO::getMobile, mobile).ne(BizUserDO::getId, id));
            if (Objects.nonNull(user))
                throw exception(BIZ_USER_MOBILE_IS_EXISTS);
            // 更新
            BizUserDO updateObj = BeanUtils.toBean(updateReqVO, BizUserDO.class);
            bizUserMapper.updateById(updateObj);
        } catch (ServiceException se) {
            throw se;
        } catch (Exception e) {
            log.error("用户新增失败", e);
            throw exception(BIZ_USER_SAVE_ERROR);
        } finally {
            if (REGISTER_LOCK.isLocked()) {
                REGISTER_LOCK.unlock();
            }
        }
    }

    @Override
    public void deleteBizUser(Long id) {
        // 校验存在
        validateBizUserExists(id);
        // 删除
        bizUserMapper.deleteById(id);
    }

    @Override
    public void deleteBizUserListByIds(List<Long> ids) {
        // 删除
        bizUserMapper.deleteByIds(ids);
    }


    private void validateBizUserExists(Long id) {
        if (bizUserMapper.selectById(id) == null) {
            throw exception(BIZ_USER_NOT_EXISTS);
        }
    }

    @Override
    public BizUserDO getBizUser(Long id) {
        return bizUserMapper.selectById(id);
    }

    @Override
    public PageResult<BizUserRespVO> getBizUserPage(BizUserPageReqVO pageReqVO) {
        PageResult<BizUserRespVO> pageResult = bizUserMapper.selectPage(pageReqVO);
        for (BizUserRespVO respVO : pageResult.getList()) {
            respVO.setExpressageCount(expressageService.getExpressageCountByUserId(respVO.getId()))
                    .setSendAddressCount(userAddressService.selectCountByUserId(respVO.getId(), 0))
                    .setRecipientsAddressCount(userAddressService.selectCountByUserId(respVO.getId(), 1));
        }
        return pageResult;
    }

    @Override
    public void updateStatus(Long userId, Boolean status) {
        bizUserMapper.update(BizUserDO::getId, userId, BizUserDO::getStatus, status);
    }

    // ============================================= APP 接口 =============================================

    @Override
    public AppAuthDTO loginByWechat(BizUserWechatLoginReqVO loginReqVO) {
        String mobile = WeChatLoginUtil.getUserMobile(loginReqVO.getMobileCode());
        if (StrUtil.isBlank(mobile))
            throw exception(WE_CHAT_MOBILE_AUTH_ERROR);

        // 根据 手机号 查询用户信息
        BizUserDO user = bizUserMapper.selectOne(BizUserDO::getMobile, mobile);

        // 若为空，则新增用户
        if (Objects.isNull(user))
            user = registerUser(mobile, null);
        else {
            if (user.getStatus())
                throw exception(BIZ_USER_ACCOUNT_DISABLE);
        }

        return authApi.appLogin(user.getId(), user.getMobile());
    }

    @Override
    public AppAuthDTO registerByMobile(BizUserMobileRegisterReqVO mobileRegisterReqVO) {
        String mobile = mobileRegisterReqVO.getMobile();
        // 判断验证码是否正确
        smsCodeApi.validateSmsCode(new SmsCodeValidateReqDTO().setMobile(mobile).setScene(MEMBER_USER_REGISTER.getScene()).setCode(mobileRegisterReqVO.getCode()));

        // 根据 手机号 查询用户信息
        BizUserDO user = bizUserMapper.selectOne(BizUserDO::getMobile, mobile);

        // 若为空，则新增用户
        if (Objects.isNull(user))
            user = registerUser(mobile, mobileRegisterReqVO.getPassword());
        else {
            if (user.getStatus())
                throw exception(BIZ_USER_ACCOUNT_DISABLE);
        }

        return authApi.appLogin(user.getId(), user.getMobile());
    }

    private BizUserDO registerUser(String mobile, String password) {
        REGISTER_LOCK.lock();
        try {
            // 判断如果存在，则直接返回
            BizUserDO user = bizUserMapper.selectOne(BizUserDO::getMobile, mobile);
            if (Objects.isNull(user)) {
                // 构建用户信息
                user = new BizUserDO();
                user.setMobile(mobile).setNickname(mobile);

                if (StrUtil.isNotBlank(password))
                    user.setPassword(authApi.passwordEncoder(password));

                // 插入用户信息
                if (bizUserMapper.insert(user) <= 0)
                    throw exception(BIZ_USER_CREATE_ERROR);
            }

            return user;
        } catch (ServiceException se) {
            throw se;
        } catch (Exception e) {
            log.error("用户注册失败", e);
            throw exception(BIZ_USER_CREATE_ERROR);
        } finally {
            if (REGISTER_LOCK.isLocked()) {
                REGISTER_LOCK.unlock();
            }
        }
    }

    @Override
    public AppAuthDTO loginByMobile(BizUserMobileLoginReqVO mobileLoginReqVO) {
        String mobile = mobileLoginReqVO.getMobile();
        String password = mobileLoginReqVO.getPassword();
        // 获取当前用户信息
        BizUserDO user = bizUserMapper.selectOne(BizUserDO::getMobile, mobile);
        if (Objects.isNull(user))
            throw exception(BIZ_USER_NOT_REGISTER);
        else {
            if (user.getStatus())
                throw exception(BIZ_USER_ACCOUNT_DISABLE);
        }

        if (StrUtil.isBlank(user.getPassword()))
            throw exception(BIZ_USER_PASSWORD_NOT_SET);

        if (!authApi.matchPassword(password, user.getPassword()))
            throw exception(BIZ_USER_PASSWORD_ERROR);

        return authApi.appLogin(user.getId(), user.getMobile());
    }

    @Override
    public void forgotPassword(BizUserMobileRegisterReqVO forgotPasswordReqVO, Long userId) {
        String mobile = forgotPasswordReqVO.getMobile();
        // 判断验证码是否正确
        smsCodeApi.validateSmsCode(new SmsCodeValidateReqDTO().setMobile(mobile).setScene(MEMBER_RESET_PASSWORD.getScene()).setCode(forgotPasswordReqVO.getCode()));

        // 获取用户信息
        BizUserDO user = bizUserMapper.selectById(userId);
        if (Objects.isNull(user))
            throw exception(BIZ_USER_NOT_REGISTER);
        else {
            if (user.getStatus())
                throw exception(BIZ_USER_ACCOUNT_DISABLE);
        }

        if (!user.getMobile().equals(mobile))
            throw exception(BIZ_USER_MOBILE_CHANGE_ERROR);

        bizUserMapper.update(BizUserDO::getId, user.getId(), BizUserDO::getPassword, authApi.passwordEncoder(forgotPasswordReqVO.getPassword()));
    }

    @Override
    public void resetPassword(BizUserMobileRegisterReqVO resetPasswordReqVO) {
        String mobile = resetPasswordReqVO.getMobile();
        // 判断验证码是否正确
        smsCodeApi.validateSmsCode(new SmsCodeValidateReqDTO().setMobile(mobile).setScene(MEMBER_RESET_PASSWORD.getScene()).setCode(resetPasswordReqVO.getCode()));

        // 获取用户信息
        BizUserDO user = bizUserMapper.selectOne(BizUserDO::getMobile, resetPasswordReqVO.getMobile());
        if (Objects.isNull(user))
            throw exception(BIZ_USER_NOT_REGISTER);
        else {
            if (user.getStatus())
                throw exception(BIZ_USER_ACCOUNT_DISABLE);
        }

        bizUserMapper.update(BizUserDO::getId, user.getId(), BizUserDO::getPassword, authApi.passwordEncoder(resetPasswordReqVO.getPassword()));
    }

    @Override
    public void mobileChange(BizUserMobileChangeReqVO mobileChangeReqVO, Long userId) {
        // 判断修改前的手机号和修改后的手机号是否一致
        String originalMobile = mobileChangeReqVO.getOriginalMobile();
        String newMobile = mobileChangeReqVO.getNewMobile();
        if (originalMobile.equals(newMobile))
            throw exception(BIZ_USER_MOBILE_CHANGE_CONSISTENT);

        // 获取用户信息
        BizUserDO user = bizUserMapper.selectById(userId);
        if (Objects.nonNull(user))
            if (user.getStatus())
                throw exception(BIZ_USER_ACCOUNT_DISABLE);
        if (!user.getMobile().equals(originalMobile))
            throw exception(BIZ_USER_MOBILE_CHANGE_ORIGINAL_ERROR);

        // 获取新手机号是否注册
        BizUserDO newMobileUser = bizUserMapper.selectOne(BizUserDO::getMobile, newMobile);
        if (Objects.nonNull(newMobileUser))
            throw exception(BIZ_USER_MOBILE_CHANGE_NEW_IS_EXISTS);

        // 判断验证是否正确
        smsCodeApi.validateSmsCode(new SmsCodeValidateReqDTO().setMobile(originalMobile).setScene(MEMBER_UPDATE_MOBILE.getScene()).setCode(mobileChangeReqVO.getOriginalCode()));
        smsCodeApi.validateSmsCode(new SmsCodeValidateReqDTO().setMobile(newMobile).setScene(MEMBER_UPDATE_MOBILE.getScene()).setCode(mobileChangeReqVO.getNewCode()));

        // 修改用户手机号

        REGISTER_LOCK.lock();
        try {
            // 判断如果存在，则直接返回
            newMobileUser = bizUserMapper.selectOne(BizUserDO::getMobile, newMobile);
            if (Objects.nonNull(newMobileUser))
                throw exception(BIZ_USER_MOBILE_CHANGE_NEW_IS_EXISTS);

            bizUserMapper.update(BizUserDO::getId, user.getId(), BizUserDO::getMobile, newMobile);
        } catch (ServiceException se) {
            throw se;
        } catch (Exception e) {
            log.error("用户手机号更换失败", e);
            throw exception(SERVICE_BUSY);
        } finally {
            if (REGISTER_LOCK.isLocked()) {
                REGISTER_LOCK.unlock();
            }
        }
    }

    @Override
    public void sendSmsCode(String mobile, Integer type) {
        int scene = MEMBER_USER_REGISTER.getScene();
        if (type.equals(2))
            scene = MEMBER_RESET_PASSWORD.getScene();
        else if (type.equals(3))
            scene = MEMBER_UPDATE_MOBILE.getScene();

        smsCodeApi.sendSmsCode(new SmsCodeSendReqDTO().setMobile(mobile)
                .setScene(scene)
                .setCreateIp(getClientIP()));
    }

    @Override
    public void appUpdateBizUser(AppBizUserUpdateReqVO updateReqVO, Long userId) {
        String avatar = updateReqVO.getAvatar();
        String nickname = updateReqVO.getNickname();
        if (StrUtil.isNotBlank(avatar) || StrUtil.isNotBlank(nickname))
            bizUserMapper.update(BizUserDO::getId, userId, BizUserDO::getNickname, nickname, BizUserDO::getAvatar, avatar);
    }
}